#include <string.h>
#include "tools.h"
#include "fs_hmac.h"

// reversing done by gray

static u8 hmac_key[20];

typedef struct{
	unsigned char key[0x40];
	SHA1Context hash_ctx;
} hmac_ctx;

static void hmac_init(hmac_ctx *ctx, const u8 *key, int key_size);
static void hmac_update(hmac_ctx *ctx, const u8 *data, int size);
static void hmac_final(hmac_ctx *ctx, u8 *hmac);


static void hmac_init(hmac_ctx *ctx, const u8 *key, int key_size)
{
	int i;

	key_size = key_size<0x40 ? key_size: 0x40;

	memset(ctx->key,0,0x40);
	memcpy(ctx->key,key,key_size);

	for(i=0;i<0x40;++i)
		ctx->key[i] ^= 0x36; // ipad

	SHA1Reset(&ctx->hash_ctx);
	SHA1Input(&ctx->hash_ctx,ctx->key,0x40);
}

static void hmac_update(hmac_ctx *ctx, const u8 *data, int size)
{
	SHA1Input(&ctx->hash_ctx,data,size);
}

static void hmac_final(hmac_ctx *ctx, unsigned char *hmac) 
{
	int i;
	unsigned char hash[0x14];

	SHA1Result(&ctx->hash_ctx);

	// this sha1 implementation is buggy, needs to switch endian
	for(i=0;i<5;++i) {
		wbe32(hash + 4*i, ctx->hash_ctx.Message_Digest[i]);
	}

	for(i=0;i<0x40;++i)
		ctx->key[i] ^= 0x36^0x5c; // opad

	SHA1Reset(&ctx->hash_ctx);
	SHA1Input(&ctx->hash_ctx,ctx->key,0x40);
	SHA1Input(&ctx->hash_ctx,hash,0x14);
	SHA1Result(&ctx->hash_ctx);

	for(i=0;i<5;++i){
		wbe32(hmac + 4*i, ctx->hash_ctx.Message_Digest[i]);
	}
}

void fs_hmac_set_key(const u8 *key, int key_size)
{
	memset(hmac_key,0,0x14);
	memcpy(hmac_key,key,key_size<0x14?key_size:0x14);
}

void fs_hmac_generic(const unsigned char *data, int size, const unsigned char *extra, int extra_size, unsigned char *hmac)
{
	hmac_ctx ctx;

	hmac_init(&ctx, hmac_key, 20);
	
	hmac_update(&ctx, extra, extra_size);
	hmac_update(&ctx, data, size);

	hmac_final(&ctx, hmac);
}

void fs_hmac_meta(const unsigned char *super_data, short super_blk, unsigned char *hmac)
{
	unsigned char extra[0x40];

	memset(extra,0,0x40);
	wbe16(extra + 0x12, super_blk);

	fs_hmac_generic(super_data,0x40000,extra,0x40,hmac);
}

void fs_hmac_data(const unsigned char *data, int uid, const unsigned char *name, int entry_n, int x3, short blk, unsigned char *hmac)
{
	unsigned char extra[0x40];

	memset(extra,0,0x40);
	
	wbe32(extra, uid);

	memcpy(extra+4,name,12);

	wbe16(extra + 0x12, blk);
	wbe32(extra + 0x14, entry_n);
	wbe32(extra + 0x18, x3);
	
	fs_hmac_generic(data,0x4000,extra,0x40,hmac);
}

